/*
 * Funny Language - a free style programming language.
 * Copyright (C) 2015 by fanguangping (fanguangping@163.com)
 * dfa.c
 */

#include <stdio.h>
#include <stdlib.h>
#include "dfa.h"
#include "../base/constants.h"
#include "../base/cmemory.h"

static HashTable stateTable = NULL;

TransitNode *alloc_transit_node() {
    return (TransitNode *) sys_malloc(sizeof(TransitNode));
}

void set_transition(State *starter, State *current, State *next, Statement *loop) {
    QueueNode *node = NULL;
    TransitNode *tnode = NULL;
    State *currentState = current;

    if (stateTable == NULL) {
        stateTable = init_table();
    }

    if (loop->children != NULL) {
        node = loop->children->head;
        while (node != loop->children->tail) {
            State *nextState = new_state();
            tnode = alloc_transit_node();
            tnode->starter = starter;
            tnode->input = (Statement *) node->element;
            tnode->next = nextState;
            put_table_element(stateTable, currentState, tnode, hash_code_state);
            currentState = nextState;

            node = node->next;
        }

        tnode = alloc_transit_node();
        tnode->starter = starter;
        tnode->input = (Statement *) loop->children->tail->element;
        tnode->next = next;
        put_table_element(stateTable, currentState, tnode, hash_code_state);
    }
}

State *to_dfa(Statement *s) {
    if (s == NULL)
        return NULL;
    State *current = new_state();
    State *starter = current;
    QueueNode *node = NULL;
    Statement *st = NULL;
    TransitNode *tnode = NULL;

    if (stateTable == NULL) {
        stateTable = init_table();
    }

    if (s->children != NULL) {
        node = s->children->head;
        while (node != NULL) {
            st = (Statement *) node->element;
            switch (st->type) {
                case SYM:
                case ARG:
                    tnode = alloc_transit_node();
                    tnode->starter = starter;
                    tnode->input = st;
                    tnode->next = new_state();
                    put_table_element(stateTable, current, tnode, hash_code_state);
                    current = tnode->next;
                    break;

                case LOOP:
                    set_transition(starter, current, current, st);
                    break;
            }
            node = node->next;
        }
        current->accept = TRUE;
    }

    return starter;
}

State *transit(State *starter, State *state, Statement *input, Queue *queue) {
    State *result = NULL;
    TransitNode *tnode = NULL;

    if (state == NULL || input == NULL)
        return NULL;
    switch (input->type) {
        case SYM:
            tnode = get_transit_node_from_table(starter, state, input);
            if (tnode != NULL) {
                result = tnode->next;
            }
            break;

        case ARG:
            tnode = get_transit_node_from_table(starter, state, input);
            if (tnode != NULL) {
                add_key_value(queue, get_arg_name(tnode->input), input);
                result = tnode->next;
            }
            break;

        case LOOP:
            fprintf(stderr, "transit error!\n");
            break;
    }

    return result;
}


HashTable getStateTable() {
    return stateTable;
}

TransitNode *get_transit_node_from_table(State *starter, State *state, Statement *input) {
    TransitNode *tnode = NULL;
    HashNode *node = (HashNode *) get_table_element(stateTable, state,
                                                    hash_code_state);
    while (node != NULL) {
        tnode = (TransitNode *) node->element;
        if (equals_state(starter, tnode->starter) && match_statement(input, tnode->input)) {
            return tnode;
        }
        node = node->next;
    }
    return NULL;
}

State *test_match(Mapping *mapping, Statement *source, Queue *repQueue) {
    State *start = mapping->starter;
    State *next = NULL;
    QueueNode *node = NULL;
    Statement *s = NULL;

    if (source->children != NULL) {
        node = source->children->head;
        while (node != NULL) {
            s = (Statement *) node->element;
            next = transit(mapping->starter, start, s, repQueue);
            start = next;
            if (start == NULL) {
                break;
            }
            node = node->next;
        }
    }

    return start;
}

